﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Netbits.Common.Collections;
using Netbits.Common.Conditions;
using Netbits.Common.Threading;
using Netbits.TelnetServer.Enums;
using Netbits.TelnetServer.Parameters;

namespace Netbits.TelnetServer
{
	/// <summary>
	/// Represents a Netbits-Telnet Folder.
	/// </summary>
	public sealed class Folder : ICloneable, IDisposable
	{
		#region Private Variables

		LockedKeyedCollection<string, Folder> _Folders;
		LockedKeyedCollection<string, Command> _Commands;
		readonly string _Name;

		bool _Disposed;
		bool _Invisible;
		string _Description;
		Folder _Parent;
		Lock _Lock;

		#endregion

		#region Constructor

		/// <summary>
		/// Creates a new instance of Folder with specified parameters.
		/// </summary>
		/// <param name="Parent_">Set the parent folder.</param>
		/// <param name="Name_">Name of this folder.</param>
		internal Folder( Folder Parent_, string Name_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();

			_Parent = Parent_;
			_Name = Name_;
			_Lock = new Lock();
			_Folders = new LockedKeyedCollection<string, Folder>( folder => folder._Name, _Lock );
			_Commands = new LockedKeyedCollection<string, Command>( command => command.Name, _Lock );
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns the folder-name
		/// </summary>
		public string Name
		{
			get
			{
				return _Name;
			}
		}

		/// <summary>
		/// Returns the folder description or set this
		/// </summary>
		public string Description
		{
			get
			{
				using( _Lock.Read() )
					return _Description;
			}
			set
			{
				using( _Lock.Write() )
					_Description = value;
			}
		}

		/// <summary>
		/// Returns the parent Folder. 
		/// If this is the main-folder the parent-folder is NULL
		/// </summary>
		public Folder Parent
		{
			get
			{
				return _Parent;
			}
		}

		/// <summary>
		/// Returns if the folder is not displayed on ? or set this.
		/// </summary>
		public bool Invisible
		{
			get
			{
				return _Invisible;
			}
			set
			{
				_Invisible = value;
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Add new folder or return existing
		/// </summary>
		/// <param name="Name_">Name of the folder</param>
		/// <returns>The specified folder</returns>
		public Folder GetFolder( string Name_ )
		{
			Folder f = _Folders[Name_];
			if( f != null )
				return f;

			f = new Folder( this, Name_ );
			_Folders.Add( f );
			_Folders.Sort();
			return f;
		}

		/// <summary>
		/// Add new Command or return existing
		/// </summary>
		/// <param name="Name_">Name of the command</param>
		/// <param name="Execute_">Execute function for this command</param>
		/// <returns>The specified command</returns>
		public Command GetCommand( string Name_, Action<NetbitsTelnetClientSession, Command> Execute_ )
		{
			return GetCommand( Name_, string.Empty, Execute_ );
		}

		/// <summary>
		/// Add new Command or return existing
		/// </summary>
		/// <param name="Name_">Name of the command</param>
		/// <param name="Execute_">Execute function for this command</param>
		/// <param name="Parameters_">List of all parameter for this command</param>
		/// <returns>The specified command</returns>
		public Command GetCommand( string Name_, Action<NetbitsTelnetClientSession, Command> Execute_, params ParameterBase[] Parameters_ )
		{
			return GetCommand( Name_, string.Empty, Execute_, Parameters_ );
		}

		/// <summary>
		/// Add new Command or return existing
		/// </summary>
		/// <param name="Name_">Name of the command</param>
		/// <param name="Description_">Description for the new command</param>
		/// <param name="Execute_">Execute function for this command</param>
		/// <param name="Parameters_">List of all parameter for this command</param>
		/// <returns>The specified command</returns>
		public Command GetCommand( string Name_, string Description_, Action<NetbitsTelnetClientSession, Command> Execute_, params ParameterBase[] Parameters_ )
		{
			Command c = _Commands[Name_];
			if( c != null )
				return c;

			c = new Command( this, Name_ );
			c.Description = Description_;
			c.Execute = Execute_;

			foreach( ParameterBase pb in Parameters_ )
				c.AddParameter( pb );

			_Commands.Add( c );
			_Commands.Sort();
			return c;
		}

		#endregion

		#region Internal Functions

		/// <summary>
		/// Print information of this folder to terminal
		/// </summary>
		/// <param name="Client_">The telnet-client session to print this information</param>
		/// <param name="Search_">Search for a beginning string</param>
		internal void PrintInfo( NetbitsTelnetClientSession Client_, string Search_ )
		{
			string folderString = string.Empty;
			folderString += Client_.GetAttribute( EnumTelnetColors.Magenta, EnumTelnetColors.Nothing, EnumTelnetAttributes.Bright );
			folderString += "{0}";
			folderString += Client_.GetAttribute( EnumTelnetColors.Yellow, EnumTelnetColors.Nothing, EnumTelnetAttributes.Dim );
			folderString += " -- ";
			folderString += Client_.GetResetColorAttributes();
			folderString += "{1}";

			string commandString = string.Empty;
			commandString += Client_.GetAttribute( EnumTelnetColors.Green, EnumTelnetColors.Nothing, EnumTelnetAttributes.Bright);
			commandString += "{0}";
			commandString += Client_.GetAttribute( EnumTelnetColors.Yellow, EnumTelnetColors.Nothing, EnumTelnetAttributes.Dim);
			commandString += " -- ";
			commandString += Client_.GetResetColorAttributes();
			commandString += "{1}";

			foreach( Folder f in _Folders )
			{
				if( !f.Invisible && f.Name.StartsWith( Search_, true, CultureInfo.CurrentCulture  ) )
					Client_.Send( folderString, f.Name, f.Description );
			}

			foreach( Command c in _Commands )
			{
				if( !c.Invisible && c.Name.StartsWith( Search_, true, CultureInfo.CurrentCulture ) )
					Client_.Send( commandString, c.Name, c.Description );
			}
		}

		/// <summary>
		/// Find the folder with the specified name.
		/// </summary>
		/// <param name="Name_">Name of the folder to find</param>
		/// <returns>represent folder or null</returns>
		internal IList<Folder> FindFolder( string Name_ )
		{
			List<Folder> folders = new List<Folder>();
			foreach( Folder f in _Folders )
			{
				if( f.Name.StartsWith( Name_, true, CultureInfo.CurrentCulture ) )
					folders.Add( f );
			}

			return folders;
		}

		/// <summary>
		/// Find the command with the specified name
		/// </summary>
		/// <param name="Name_">Name of the command to find</param>
		/// <returns>represent command or null</returns>
		internal IList<Command> FindCommand( string Name_ )
		{
			List<Command> commands = new List<Command>();
			foreach( Command c in _Commands )
			{
				if( c.Name.StartsWith( Name_, true, CultureInfo.CurrentCulture ) )
					commands.Add( c );
			}

			return commands;
		}

		/// <summary>
		/// Get the correct full (all sub-folders) name
		/// </summary>
		/// <returns>Full Foldername</returns>
		internal string GetFullFoldername()
		{
			Folder f = this;
			StringBuilder name = new StringBuilder();

			if( f.Parent == null )
				return "/";

			while( f.Parent != null )
			{
				name.Insert( 0, f.Name );
				name.Insert( 0, '/' );
				f = f.Parent;
			}

			return name.ToString();
		}

		#endregion

		#region ICloneable

		/// <summary>
		/// Makes a copy from this folder with all subfolders and commands.
		/// </summary>
		/// <returns>new Folder</returns>
		public object Clone()
		{
			Folder f = new Folder( _Parent, _Name );
			f.Description = _Description;
			f.Invisible = _Invisible;

			foreach( Folder childFolder in _Folders )
			{
				Folder newf = childFolder.Clone() as Folder;
				newf._Parent = f;
				f._Folders.Add( newf );
			}

			foreach( Command childCommand in _Commands )
			{
				Command newc = childCommand.Clone() as Command;
				newc.Parent = f;
				f._Commands.Add( newc );
			}

			return f;
		}

		#endregion

		#region IDisposable

		/// <summary>
		/// Dispose folder resources.
		/// </summary>
		public void Dispose()
		{
			if( !_Disposed )
			{
				if( _Folders != null )
				{
					foreach( Folder f in _Folders )
						f.Dispose();
					_Folders.Clear();
					_Folders = null;
				}

				if( _Commands != null )
				{
					foreach( Command c in _Commands )
						c.Dispose();
					_Commands.Clear();
					_Commands = null;
				}

				if( _Lock != null )
				{
					_Lock.Dispose();
					_Lock = null;
				}

				_Parent = null;
				_Disposed = true;
			}

			GC.SuppressFinalize( this );
		}

		#endregion
	}
}
